/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-use-before-define */
import type { Button, FormInstance, FormItemProps, Select, SelectProps } from 'antd';
import { memo, useContext, useEffect, useImperativeHandle, useRef } from 'react';
import styled from 'styled-components';
import type { ActionRefType, ChildrenItemFunction, CreateOperation, DataType, FormOperation, Operation, ExtraContextType, CreateFieldType, SecondArgType, NodeFieldType, FieldNames } from '../types';
import { clsx, get, normalizeGap, toNamePath, wrapperCls, uuid, shallow, omitUndefined, omit, getRelationPath, fillFieldNames, toBasePath } from '../utils';
import RuleNode from './RuleNode';
import { RuleTreeContext, RuleTreePropsContext } from './RuleTreeContext';
import { DndProvider, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { addRuleGroup, addRuleItem, moveRuleItem, removeRuleItem } from '../utils/model';
import { DefaultTreeValue, DragItemType } from '../utils/constant';
import DragLayer from './DragLayer';
import { FormItemProvide } from '@alipay/tech-ui';
import { useOutterComponents, useDragRootElement } from '../utils/hooks';
import type { Locale } from '../locale';
import { fieldNamesTreeMap } from '../utils/global';
export type RuleTreeProps<T = any> = {
  /** 类名前缀 */
  prefixCls?: string
  /** 国际化配置 */
  locale?: Locale
  /** 默认值 */
  defaultValue?: DataType<T>
  /** 默认relation的值 */
  defaultRelationValue?: string | number
  /** 值，受控属性 */
  value?: DataType<T>
  /** 自定义 relation、children 字段 */
  fieldNames?: FieldNames
  /** 连接线的颜色 */
  lineColor?: string
  /** 值发生变动时的事件 */
  onChange?: (value: DataType<T>) => void
  children: ChildrenItemFunction<T>
  className?: string
  style?: React.CSSProperties
  /** 自定义relation，返回必须是一个表单项 */
  relationRender?: (dom: React.ReactElement<React.ComponentProps<typeof Select>>, fields: NodeFieldType) => React.ReactNode
  /** relation表单项的props */
  relationFormItemProps?: FormItemProps
  /** relation表单项的props */
  relationFormItemRender?: (dom: React.ReactElement<FormItemProps>, fields: NodeFieldType) => React.ReactNode
  /** node 自定义渲染 */
  nodeRender?: (doms: {
    relationDom: React.ReactNode
    listDom: React.ReactNode
  }, fields: NodeFieldType) => React.ReactNode
  /** relation改变时的事件 */
  onRelationChange?: (relationValue: number | string, option: SecondArgType<SelectProps['onChange']>, fields: NodeFieldType) => void
  // TODO
  /** 自定义result，返回必须是一个表单项 */
  // resultRender?: () => React.ReactNode
  disabled?: boolean
  /** 可拖拽 */
  draggable?: boolean | ((fields: { levelPath: number[] }) => boolean)
  /** 获取组件内部的form instance */
  formRef?: React.Ref<FormInstance<T> | undefined>
  /** 获取组件action handler */
  actionRef?: React.Ref<ActionRefType<T> | undefined>
  /** 间隔 */
  gap?: number | [number, number]
  /** 最大深度 */
  maxDeepth?: number
  /** 最大数量限制 */
  max?: number
  /** 自定义 dragLayer */
  dragLayerRender?: (dom: React.ReactNode) => React.ReactNode
  /**
   * 重新布局rule-item
   * @param doms
   * @param record 当前项的值
   * @param fields 不常用的属性收敛在这个字段里
   */
  itemRender?: (doms: {
    itemDom: React.ReactNode
    actionDom: React.ReactNode
  }, record: T, fields: ExtraContextType) => React.ReactNode
  /**
   * 自定义action
   * @param doms
   * @param action handler
   * @param fields 不常用的属性收敛在这个字段里
   */
  actionRender?: (doms: {
    copyDom: React.ReactElement<React.ComponentProps<typeof Button>>
    removeDom: React.ReactElement<React.ComponentProps<typeof Button>>
  }, action: Operation<T> & FormOperation<T>, fields: ExtraContextType) => React.ReactNode[]
  /**
   * 设置底部规则添加按钮
   * @param doms
   * @param action handle
   * @param fields 不常用的属性收敛在这个字段里
   */
  createRender?: (doms: {
    addDom: React.ReactElement<React.ComponentProps<typeof Button>>
    addGroupDom: React.ReactElement<React.ComponentProps<typeof Button>>
  }, action: CreateOperation<T>, fields: CreateFieldType) => React.ReactNode[]
}

const WrapperRuleTree = styled.div<{
  $cls: string
  $gap: [number, number]
  $lineColor: string
}>`
  padding: 24px;
  ${props => '.' + props.$cls} {
    &-item {
      position: relative;
      &::before {
        content: '';
        display: block;
        position: absolute;
        border: 1px solid ${props => props.$lineColor};
        height: 100%;
        left: -${props => props.$gap[0] / 2}px;
        top: 0;
      }
      &:first-child::before {
        top: calc(50% - ${p => p.$gap[1] / 2}px);
        height: calc(50% + ${p => p.$gap[1] / 2}px);
      }
      &:last-child::before {
        height: calc(50% - ${p => p.$gap[1] / 2}px);
      }
      &-ctrl::before {
        height: 50%!important;
      }
      &:first-child:last-child::before {
        content: none;
      }
      &-drag::before {
        content: '';
        display: block;
        position: absolute;
        border: 1px solid ${props => props.$lineColor};
        width: ${props => props.$gap[0] / 2 - 2}px;
        left: -${props => props.$gap[0] / 2}px;
        top: calc(50% - ${p => p.$gap[1] / 2}px);
      }
      &-create::before {
        content: '';
        display: block;
        position: absolute;
        border: 1px solid ${props => props.$lineColor};
        width: ${props => props.$gap[0] / 2 - 2}px;
        left: -${props => props.$gap[0] / 2}px;
        top: 50%;
      }
      &:last-child:not(${p => '.' + p.$cls + '-item-ctrl'}) {
        &::before {
          height: 50%;
        }/* rule-tree-item-drag */
        & > ${p => '.' + p.$cls + '-item-drag'}::before {
          top: 50%;
        }
        /* rule-tree-item-gap */
        & > div:last-child {
          display: none;
        }
      }
    }
    &-relation {
      position: relative;
      &::after {
        content: '';
        display: block;
        position: absolute;
        border: 1px solid ${props => props.$lineColor};
        width: ${props => props.$gap[0] / 2 - 2}px;
        right: -${props => props.$gap[0] / 2}px;
        top: 50%;
      }
    }

    /* &-item {
      &::after {
        content: '';
        display: block;
        position: absolute;
        border: 1px solid ${props => props.$lineColor};
        height: 100%;
        right: -${props => props.$gap[0] / 2}px;
        top: 0;
      }
      &:first-child::after {
        top: calc(50% - ${p => p.$gap[1] / 2}px);
        height: calc(50% + ${p => p.$gap[1] / 2}px);
      }
      &:last-child::after {
        height: calc(50% - ${p => p.$gap[1] / 2}px);
      }
      &-ctrl::after {
        height: 50%!important;
      }
      &:first-child:last-child::after {
        content: none;
      }
      &-drag::after {
        content: '';
        display: block;
        position: absolute;
        border: 1px solid ${props => props.$lineColor};
        width: ${props => props.$gap[0] / 2 - 2}px;
        right: -${props => props.$gap[0] / 2}px;
        top: calc(50% - ${p => p.$gap[1] / 2}px);
      }
      &-create::after {
        content: '';
        display: block;
        position: absolute;
        border: 1px solid ${props => props.$lineColor};
        width: ${props => props.$gap[0] / 2 - 2}px;
        right: -${props => props.$gap[0] / 2}px;
        top: 50%;
      }
    }
    &-result {
      position: relative;
      &::after {
        content: '';
        display: block;
        position: absolute;
        border: 1px solid ${props => props.$lineColor};
        width: ${props => props.$gap[0] / 2 - 2}px;
        left: -${props => props.$gap[0] / 2}px;
        top: 50%;
      }
    } */

    &-list {
      position: relative;
      display: flex;
      flex-direction: column;
    }

    &-indicator {
      position: absolute;
      border-radius: 4px;
      top: -16px;
      background-color: inherit;
      width: 50px;
      height: 16px;
    }
  }
`;

const WrapperRuleTreeDrop = (props: {
  lineColor: string
  gap: [number, number]
  cls: string
  className?: string
  style?: React.CSSProperties
  children?: React.ReactNode
}) => {

  // notice: 目的是解决由于自定义拖拽预览drop时没动画带来的卡顿
  const [, drop] = useDrop(() => {
    return {
      accept: DragItemType,
    };
  });

  return (
    <WrapperRuleTree
      ref={drop}
      className={props.className}
      style={props.style}
      $lineColor={props.lineColor}
      $cls={props.cls}
      $gap={props.gap}
    >
      {props.children}
    </WrapperRuleTree>);
};

function RuleTreeCore<T>(props: RuleTreeProps<T> & {
  uid: string;
  /** rule-tree内部维护的form */
  form?: FormInstance
}) {
  const { Form, isProForm } = useOutterComponents();
  const [form] = Form.useForm(props.form);
  // const { name } = useContext(FormItemProvide);
  const configStore = useContext(RuleTreeContext);
  const cls = wrapperCls(configStore.prefixCls || props.prefixCls);
  const gap = normalizeGap(props.gap);
  // 解决context传入非window时拖拽报错ref.clearTimeout is not a function，详见：https://github.com/react-dnd/react-dnd/issues/3590
  const backendContextRef = useRef({
    clearTimeout: window.clearTimeout.bind(window),
    document,
  });
  // 解决与原生dragable冲突问题，原因见该hooks定义处描述
  const { rootElement, loadRootElement } = useDragRootElement();
  const { relation: RelationKey, children: ChildrenKey } = fieldNamesTreeMap.get(props.uid) as Required<FieldNames>;

  useImperativeHandle(props.formRef, () => form);
  useImperativeHandle(props.actionRef, () => {
    return {
      add(path, data, insertIndex) {
        const newPath = toNamePath(path, ChildrenKey);
        addRuleItem(form, newPath, data, insertIndex);
        props.onChange?.(form.getFieldsValue(true));
      },
      remove(path, removeIndex) {
        const newPath = toNamePath(path, ChildrenKey);
        removeRuleItem(form, newPath, removeIndex, path.length === 0);
        props.onChange?.(form.getFieldsValue(true));
      },
      move(from, to) {
        moveRuleItem(form, from, to, ChildrenKey);
        props.onChange?.(form.getFieldsValue(true));
      },
      get(path) {
        const newPath = toNamePath(path, ChildrenKey);
        const storeValue = form.getFieldsValue(true);
        return get(storeValue, newPath);
      },
      addGroup(path, defaultGroupData, insertIndex) {
        const newPath = toNamePath(path, ChildrenKey);
        const mergedGroupData = defaultGroupData || { [RelationKey]: props.defaultRelationValue };
        addRuleGroup(form, newPath, ChildrenKey, mergedGroupData, insertIndex);
        props.onChange?.(form.getFieldsValue(true));
      },
      getGroupData(levelPath) {
        const path = toNamePath(levelPath, ChildrenKey);
        return {
          [RelationKey]: form.getFieldValue(getRelationPath(path, RelationKey)),
          [ChildrenKey]: form.getFieldValue(path),
        };
      },
    };
  });
  return (
    <div className={`${cls}-wrapper`} ref={loadRootElement}>
      {
        rootElement && (
          <DndProvider
            backend={HTML5Backend}
            options={{ rootElement }}
            context={backendContextRef.current}
          >
            <RuleTreeContext.Provider
              value={{
                prefixCls: props.prefixCls,
                locale: props.locale,
                rootPath: name ? [name] : undefined,
                ...omitUndefined(configStore),
              }}
            >
              <RuleTreePropsContext.Provider
                value={{
                  children: props.children,
                  onChange: props.onChange,
                  onRelationChange: props.onRelationChange,
                  max: props.max,
                  maxDeepth: props.maxDeepth,
                  gap: gap,
                  createRender: props.createRender,
                  disabled: props.disabled,
                  actionRender: props.actionRender,
                  itemRender: props.itemRender,
                  dragLayerRender: props.dragLayerRender,
                  relationRender: props.relationRender,
                  relationFormItemProps: props.relationFormItemProps,
                  relationFormItemRender: props.relationFormItemRender,
                  defaultRelationValue: props.defaultRelationValue,
                  // resultRender: props.resultRender,
                  draggable: props.draggable,
                  nodeRender: props.nodeRender,
                  uid: props.uid,
                }}
              >
                <WrapperRuleTreeDrop
                  lineColor={props.lineColor || '#eee'}
                  gap={gap}
                  cls={cls}
                  className={clsx(cls, props.className)}
                  style={props.style}
                >
                  <Form
                    // @ts-ignore
                    submitter={isProForm ? false : undefined}
                    form={form}
                    disabled={props.disabled}
                    component={false}
                    onValuesChange={() => {
                      // notice: 设置为true, 返回原始的数据引用，不拷贝
                      const value = form.getFieldsValue(true);
                      props.onChange?.(value);
                    }}
                  >
                    <RuleNode<T> name={null} path={[]} isRoot />
                  </Form>
                </WrapperRuleTreeDrop>
                <DragLayer />
              </RuleTreePropsContext.Provider>
            </RuleTreeContext.Provider>
          </DndProvider>
        )
      }
    </div>

  );
}

const MemoRuleTree = memo(RuleTreeCore, (prev, next) => shallow(prev, next, ['style', 'gap'])) as typeof RuleTreeCore;

RuleTree.toNamePath = toNamePath;
RuleTree.toBasePath = toBasePath;
RuleTree.getRelationPath = getRelationPath;

export default function RuleTree<T>(props: RuleTreeProps<T>) {
  const uidRef = useRef(uuid());
  const fieldNames = fillFieldNames(props.fieldNames);
  const { children: ChildrenKey } = fieldNames;
  fieldNamesTreeMap.set(uidRef.current, fieldNames);

  const defaultTreeValue = props.defaultValue || DefaultTreeValue(ChildrenKey);
  const { Form } = useOutterComponents();
  const [form] = Form.useForm();

  useEffect(() => {
    if (props.value !== form.getFieldsValue(true)) {
      form.setFieldsValue(props.value || defaultTreeValue);
    }
    // 这里不能传其他state
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [props.value]);


  return (
    <MemoRuleTree
      {...omit(props, ['defaultValue', 'value']) as RuleTreeProps}
      uid={uidRef.current}
      onRelationChange={props.onRelationChange}
      onChange={props.onChange}
      form={form}
    />
  );
}